#include "udpServer.hpp"
#include "OnlineUser.hpp"
#include <memory>
#include <fstream>
#include <unordered_map>

// 服务端

// 做的业务：从客服端获取到一个英文单词，翻译之后，再发回到客服端
using namespace std;
using namespace Server;

const std::string dictTxt = "./dict.txt";

static void Usage(string proc)
{
    cout << "Usage:\n\t" << proc << "local_ip local_port\n\n";
}

unordered_map<string, string> dict;

static bool cutString(const string &target, string *s1, string *s2, const string sep)
{
    // apple:苹果
    auto pos = target.find(sep);
    if (pos == string::npos)
        return false;
    *s1 = target.substr(0, pos);
    *s2 = target.substr(pos + sep.size());
}
static void initDict()
{
    // 这里用的是c++文件操作
    ifstream in(dictTxt, std::ios::binary);
    if (!in.is_open())
    {
        std::cerr << "open file" << dictTxt << "error" << endl;
        exit(OPEN_ERR);
    }
    string line;
    string key, value;
    while (getline(in, line))
    {
        // cout << line << endl;
        if (cutString(line, &key, &value, ":"))
        {
            dict.insert(make_pair(key, value));
        }
    }
    in.close();

    cout << "load dict success" << endl;
}

static void debugPrint()
{
    for (auto &dt : dict)
    {
        cout << dt.first << "#" << dt.second << endl;
    }
}

// dome1
void handlerMessage(int sockfd, string clientip, uint16_t clientport, string messages)
{
    // 这里就可以对message进行特定的业务处理，而不关心message怎么来的 ---- server通信和业务逻辑解耦！！

    // 婴儿版的业务逻辑
    string response_message;
    auto iter = dict.find(messages);
    if (iter == dict.end())
        response_message = "unknown";
    else
        response_message = iter->second;

    // 现在要把翻译过后的数据发送过去
    struct sockaddr_in client;
    bzero(&client, sizeof(client));

    client.sin_family = AF_INET;
    client.sin_port = htons(clientport);
    client.sin_addr.s_addr = inet_addr(clientip.c_str());

    sendto(sockfd, response_message.c_str(), response_message.size(), 0, (struct sockaddr *)&client, sizeof(client));
}
// 这个在客户端输入命令，然后在执行服务端执行
// demo2
void execCommand(int sockfd, string clientip, uint16_t clientport, string cmd)
{
    // 1. cmd 解析 ls -a -l
    // 2. 如果必要，可能需要fork
    if (cmd.find("rm") != string::npos || cmd.find("mv") != string::npos || cmd.find("rmdir") != string::npos)
    {
        cerr << clientip << ":" << clientport << "正在做一个非法的操作: " << cmd << endl;
        return;
    }
    string response;
    FILE *fp = popen(cmd.c_str(), "r");
    if (fp == nullptr)
        response = cmd + "exec failed";
    char line[1024];
    while (fgets(line, sizeof(line), fp))
    {
        response += line;
    }
    // 开始返回
    struct sockaddr_in client;
    bzero(&client, sizeof(client));

    client.sin_family = AF_INET;
    client.sin_port = htons(clientport);
    client.sin_addr.s_addr = inet_addr(clientip.c_str());

    sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&client, sizeof(client));

    pclose(fp);
}

// dome3

OnlineUser onlineuser;

void routeMessage(int sockfd, string clientip, uint16_t clientport, string messages)
{
    // 你是否上线，你发送online，就表示你上线了
    if (messages == "online")
        onlineuser.addUser(clientip, clientport);
    // 下线
    if (messages == "offline")
        onlineuser.delUser(clientip, clientport);
    // 判断你是否上线，上线就可以发消息了
    if (onlineuser.isOnline(clientip, clientport))
    {
        onlineuser.broadcastMessage(sockfd, clientip, clientport, messages);
    }
    // 不在线，就返回一条消息给你
    else
    {
        struct sockaddr_in client;
        bzero(&client, sizeof(client)); // 把字节清零，然后重新绑定ip+端口号

        client.sin_family = AF_INET;         // 表示使用ipv4协议进行通讯  sin_family设置地址族
        client.sin_port = htons(clientport); // 设置端口号 htons把 主机字节序转化为网络字节序，因为不确定主机的大小端，但是网络中都是大端的
        // inet_addr 用于将点分十进制表示的 IPv4 地址字符串（如 "192.168.1.1"）转换为适合网络通信使用的 32 位无符号整数
        client.sin_addr.s_addr = inet_addr(clientip.c_str());

        string response = "你还没有上线，请先上线，运行：online";

        // 发消息
        /*ssize_t sendto(int socket, const void *message, size_t length,
              int flags, const struct sockaddr *dest_addr,
              socklen_t dest_len);*/
        sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&client, sizeof(client));
    }
}
// ./udpServer ip port 通过命令行的方式把ip和端口号传进来
int main(int argc, char *argv[])
{

    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);

    // std::unique_ptr<udpServer> usvr(new udpServer(handlerMessage, port)); // dome1
    // std::unique_ptr<udpServer> usvr(new udpServer(execCommand, port)); // dome2
    std::unique_ptr<udpServer> usvr(new udpServer(routeMessage, port)); // dome3

    usvr->initServer();
    usvr->start();
    return 0;
}